package cn.redsoft.wechatapi.common.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.redsoft.wechatapi.common.Consts;
import cn.redsoft.wechatapi.common.exception.UnKnowMsgTypeException;
import cn.redsoft.wechatapi.msg.req.RequestMsg;
import cn.redsoft.wechatapi.msg.req.event.EventRequestMsg;
import cn.redsoft.wechatapi.msg.req.event.LocationEventRequestMsg;
import cn.redsoft.wechatapi.msg.req.event.MenuClickEventRequestMsg;
import cn.redsoft.wechatapi.msg.req.event.MenuViewEventRequestMsg;
import cn.redsoft.wechatapi.msg.req.event.SubscribeEventRequestMsg;
import cn.redsoft.wechatapi.msg.req.event.UnsubscribeEventRequestMsg;
import cn.redsoft.wechatapi.msg.req.generic.ImageRequestMsg;
import cn.redsoft.wechatapi.msg.req.generic.LinkRequestMsg;
import cn.redsoft.wechatapi.msg.req.generic.LocationRequestMsg;
import cn.redsoft.wechatapi.msg.req.generic.ShortVideoRequestMsg;
import cn.redsoft.wechatapi.msg.req.generic.TextRequestMsg;
import cn.redsoft.wechatapi.msg.req.generic.VideoRequestMsg;
import cn.redsoft.wechatapi.msg.req.generic.VoiceRequestMsg;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

public class XMLParser {
	protected Logger log = LoggerFactory.getLogger(XMLParser.class);
	private static Map<String, Class<? extends RequestMsg>> map = new HashMap<String, Class<? extends RequestMsg>>();
	private static Map<String, Class<? extends EventRequestMsg>> eventMsgMap = new HashMap<String, Class<? extends EventRequestMsg>>();

	static {
		// 普通消息7种
		map.put(MsgUtil.REQ_MSG_IMAGE, ImageRequestMsg.class);
		map.put(MsgUtil.REQ_MSG_LINK, LinkRequestMsg.class);
		map.put(MsgUtil.REQ_MSG_LOCATION, LocationRequestMsg.class);
		map.put(MsgUtil.REQ_MSG_VIDEO, VideoRequestMsg.class);
		map.put(MsgUtil.REQ_MSG_VOICE, VoiceRequestMsg.class);
		map.put(MsgUtil.REQ_MSG_TEXT, TextRequestMsg.class);
		map.put(MsgUtil.REQ_MSG_SHORT_VEDIO, ShortVideoRequestMsg.class);

		map.put(MsgUtil.REQ_MSG_EVENT, EventRequestMsg.class);

		// 事件类型消息7种
		// 地理位置
		eventMsgMap.put(MsgUtil.REQ_MSG_EVENT_LOCATION,
				LocationEventRequestMsg.class);
		// 关注
		eventMsgMap.put(MsgUtil.REQ_MSG_EVENT_SUBSCRIBE,
				SubscribeEventRequestMsg.class);
		// 取消关注
		eventMsgMap.put(MsgUtil.REQ_MSG_EVENT_UN_SUBSCRIBE,
				UnsubscribeEventRequestMsg.class);
		// MenuClick
		eventMsgMap.put(MsgUtil.REQ_MSG_EVENT_MENU_CLICK,
				MenuClickEventRequestMsg.class);
		// MenuView
		eventMsgMap.put(MsgUtil.REQ_MSG_EVENT_MENU_VIEW,
				MenuViewEventRequestMsg.class);

		// 扫码--未关注
		// eventMsgMap.put(MsgUtil.QR_CODE_SCANNING_UNSUBSCRIBE,
		// QRCodeScanningUnSubscribeEventRequestMsg.class);

		// 扫码--已关注
		// eventMsgMap.put(MsgUtil.QR_CODE_SCANNING_SUBSCRIBED,
		// QRCodeScanningSubscribedEventRequestMsg.class);
	}

	public String getXMLStringFromRequest(HttpServletRequest request)
			throws IOException {
		StringBuilder sb = new StringBuilder();
		InputStream is = request.getInputStream();
		InputStreamReader isr = new InputStreamReader(is,
				Consts.CHARACTER_ENCODING);
		BufferedReader br = new BufferedReader(isr);
		String s = null;
		while ((s = br.readLine()) != null) {
			sb.append(s);
		}
		br.close();
		isr.close();
		is.close();
		if (log.isDebugEnabled()) {
			log.debug("<=============================== : ");
			log.debug("<== 解析请求开始 : ");
			log.debug("\t<== 1. 请求消息 xml 字符串: {} ", sb.toString());
		}
		return sb.toString();
	}

	private Class<? extends RequestMsg> getEventMsgType(String t)
			throws UnKnowMsgTypeException {
		Class<? extends EventRequestMsg> clazz = eventMsgMap.get(t);
		if (clazz == null)
			throw new UnKnowMsgTypeException("未知消息类型 : " + t);
		return clazz;
	}

	private Class<? extends RequestMsg> getType(String t)
			throws UnKnowMsgTypeException {
		Class<? extends RequestMsg> clazz = map.get(t);
		if (clazz == null)
			throw new UnKnowMsgTypeException("未知消息类型 : " + t);
		return clazz;
	}

	public Class<? extends RequestMsg> getMsgJavaType(String strXml)
			throws DocumentException, UnKnowMsgTypeException {
		Document document = DocumentHelper.parseText(strXml);
		// 获得文档的根节点
		Element root = document.getRootElement();
		Element element = root.element("Event");
		if (element != null) {// 事件消息
			log.debug("\t<== 2. 请求消息类型解析成功 : 【<= event({})】 .", element.getText()
					.trim());
			return this.getEventMsgType(element.getText().trim());
		}

		// 普通消息
		// 遍历根节点下所有子节点
		Iterator<?> iter = root.elementIterator();
		while (iter.hasNext()) {
			Element ele = (Element) iter.next();
			if (ele.getName().equals("MsgType")) {
				log.debug("\t<== 2. 请求消息类型解析成功 : 【<= {}】 .", ele.getText().trim());
				return this.getType(ele.getText().trim());
			}
		}
		return null;
	}

	public RequestMsg parseMessage(HttpServletRequest request)
			throws IOException, UnKnowMsgTypeException, DocumentException {
		return this
				.parseMsgFromXMLString(this.getXMLStringFromRequest(request));
	}

	public RequestMsg parseMsgFromXMLString(String xml)
			throws UnKnowMsgTypeException, DocumentException {
		XStream xstream = new XStream(new DomDriver());
		Class<? extends RequestMsg> cls = this.getMsgJavaType(xml);
		xstream.processAnnotations(cls);

		RequestMsg req = (RequestMsg) xstream.fromXML(xml);
		if (log.isDebugEnabled()) {
			log.debug("\t<== 3. xml-->MsgBean: {} .", req);
			log.debug("<== 请求解析完毕 .");
		}
		return req;
	}

	public static String formatString(String str) throws Exception {
		Document document = null;
		document = DocumentHelper.parseText(str);
		// 格式化输出格式
		OutputFormat format = OutputFormat.createPrettyPrint();
		format.setEncoding(Consts.CHARACTER_ENCODING);
		StringWriter writer = new StringWriter();
		// 格式化输出流
		XMLWriter xmlWriter = new XMLWriter(writer, format);
		// 将document写入到输出流
		xmlWriter.write(document);
		xmlWriter.close();
		return writer.toString();
	}
}
